// NetFolder.cpp

#include "StdAfx.h"

#include "Common/MS/PropVariant.h"

#include "Common/7Zip/PropID.h"

#include "FSFolder.h"
#include "NetFolder.h"
#include "SysIconUtils.h"

namespace Common
{
namespace FileSystem
{
namespace Net {

static const STATPROPSTG kProps[] =
{
  { NULL, kpidName, VT_BSTR},
  { NULL, kpidLocalName, VT_BSTR},
  { NULL, kpidComment, VT_BSTR},
  { NULL, kpidProvider, VT_BSTR}
};

HRESULT CNetFolder::Init(const CString &path)
{
  /*
  if (path.Length() > 2)
  {
    if (path[0] == _T('\\') && path[1] == _T('\\'))
    {
      CResource netResource;
      netResource.RemoteName = GetSystemString(path.Left(path.Length() - 1));
      netResource.Scope = RESOURCE_GLOBALNET;
      netResource.Type = RESOURCETYPE_DISK;
      netResource.DisplayType = RESOURCEDISPLAYTYPE_SERVER;
      netResource.Usage = RESOURCEUSAGE_CONTAINER;
      Init(&netResource, 0, path);
      return;
    }
  }
  Init(0, 0 , _T(""));
  */
	std::shared_ptr<CResource> resource = std::make_shared<CResource>();
  resource->RemoteNameIsDefined = true;
  resource->RemoteName = path.Left(path.GetLength() - 1);
  resource->ProviderIsDefined = false;
  resource->LocalNameIsDefined = false;
  resource->CommentIsDefined = false;
  resource->Type = RESOURCETYPE_DISK;
  resource->Scope = RESOURCE_GLOBALNET;
  resource->Usage = 0;
  resource->DisplayType = 0;
  std::shared_ptr<CResource> destResource = std::make_shared<CResource>();
  CString systemPathPart;
  DWORD result = GetResourceInformation(resource.get(), destResource.get(), systemPathPart);
  if (result == NO_ERROR)
    return Init(destResource, NULL, path);
  else
    return Init(0, 0 , _T(""));
}

HRESULT CNetFolder::Init(const std::shared_ptr<CResource> & netResource,
      IFolderFolder *parentFolder, const CString &path)
{
  _path = path;
  if (netResource == NULL)
    //_netResourcePointer = NULL;
	_netResource = NULL;
  else
  {
    _netResource = netResource;
    //_netResourcePointer = &_netResource;

    // if (_netResource.DisplayType == RESOURCEDISPLAYTYPE_SERVER)
    _path = _netResource->RemoteName + CHAR_PATH_SEPARATOR;
  }
  _parentFolder = parentFolder;
  return LoadItems();
}

STDMETHODIMP CNetFolder::LoadItems()
{
  _items.clear();
  CEnum enumerator;

  for (;;)
  {
    DWORD result = enumerator.Open(
      RESOURCE_GLOBALNET,
      RESOURCETYPE_DISK,
      0,        // enumerate all resources
	  _netResource.get()
      );
    if (result == NO_ERROR)
      break;
    if (result != ERROR_ACCESS_DENIED)
      return result;
    if (_netResource != NULL)
		result = AddConnection2(_netResource.get(),
        0, 0, CONNECT_INTERACTIVE);
    if (result != NO_ERROR)
      return result;
  }

  for (;;)
  {
	  std::shared_ptr<CResourceEx> resource = std::make_shared<CResourceEx>() ;
    DWORD result = enumerator.Next(*resource);
    if (result == NO_ERROR)
    {
      if (!resource->RemoteNameIsDefined) // For Win 98, I don't know what's wrong
        resource->RemoteName = resource->Comment;
      resource->Name = resource->RemoteName;
      int pos = resource->Name.ReverseFind(CHAR_PATH_SEPARATOR);
      if (pos >= 0)
      {
        // _path = resource.Name.Left(pos + 1);
        resource->Name = resource->Name.Mid(pos + 1);
      }
	  
	  _items.push_back(resource);
    }
    else if (result == ERROR_NO_MORE_ITEMS)
      break;
    else
      return result;
  }

  /*
  It's too slow for some systems.
  if (_netResourcePointer && _netResource.DisplayType == RESOURCEDISPLAYTYPE_SERVER)
  {
    for (char c = 'a'; c <= 'z'; c++)
    {
      CResourceEx resource;
      resource.Name = CString(wchar_t(c)) + _T('$');
      resource.RemoteNameIsDefined = true;
      resource.RemoteName = _path + resource.Name;

      NFile::NFind::CFindFile findFile;
      NFile::NFind::CFileInfoW fileInfo;
      if (!findFile.FindFirst(resource.RemoteName + CString(CHAR_PATH_SEPARATOR) + CString(_T("*")), fileInfo))
        continue;
      resource.Usage = RESOURCEUSAGE_CONNECTABLE;
      resource.LocalNameIsDefined = false;
      resource.CommentIsDefined = false;
      resource.ProviderIsDefined = false;
      _items.Add(resource);
    }
  }
  */
  return S_OK;
}


STDMETHODIMP CNetFolder::GetNumberOfItems(UINT32 *numItems)
{
  *numItems = _items.size();
  return S_OK;
}

STDMETHODIMP CNetFolder::GetProperty(UINT32 itemIndex, PROPID propID, PROPVARIANT *value)
{
  CComPropVariant prop;
  const auto & item = _items[itemIndex];
  switch(propID)
  {
    case kpidIsDir:  prop = true; break;
    case kpidName:
      // if (item.RemoteNameIsDefined)
        prop = CComBSTR(item->Name);
      break;
    case kpidLocalName:  if (item->LocalNameIsDefined) prop = CComBSTR(item->LocalName); break;
    case kpidComment: if (item->CommentIsDefined) prop = CComBSTR(item->Comment); break;
    case kpidProvider: if (item->ProviderIsDefined) prop = CComBSTR(item->Provider); break;
  }
  prop.Detach(value);
  return S_OK;
}

STDMETHODIMP CNetFolder::BindToFolder(UINT32 index, IFolderFolder **resultFolder)
{
  *resultFolder = 0;
  const auto & resource = _items[index];
  if (resource->Usage == RESOURCEUSAGE_CONNECTABLE ||
      resource->DisplayType == RESOURCEDISPLAYTYPE_SHARE)
  {
    FileSystem::Folder::CFSFolder *fsFolderSpec = new CComObject<FileSystem::Folder::CFSFolder>;
	fsFolderSpec->FinalConstruct();
    CComPtr<IFolderFolder> subFolder = fsFolderSpec;
    HRESULT hRes(fsFolderSpec->Init(resource->RemoteName + CHAR_PATH_SEPARATOR, this));
	if(S_OK != hRes)
		return hRes;
    *resultFolder = subFolder.Detach();
  }
  else
  {
    CNetFolder *netFolder = new CComObject<CNetFolder>;
	netFolder->FinalConstruct();
    CComPtr<IFolderFolder> subFolder = netFolder;
    netFolder->Init(resource, this, resource->Name + CHAR_PATH_SEPARATOR);
    *resultFolder = subFolder.Detach();
  }
  return S_OK;
}

STDMETHODIMP CNetFolder::BindToFolder(const wchar_t * /* name */, IFolderFolder ** /* resultFolder */)
{
  return E_NOTIMPL;
}

STDMETHODIMP CNetFolder::BindToParentFolder(IFolderFolder **resultFolder)
{
  *resultFolder = 0;
  if (_parentFolder)
  {
    CComPtr<IFolderFolder> parentFolder = _parentFolder;
    *resultFolder = parentFolder.Detach();
    return S_OK;
  }
  if (_netResource != NULL)
  {
	  std::shared_ptr<CResource> resourceParent = std::make_shared<CResource>();
	  DWORD result = GetResourceParent(_netResource.get(), resourceParent.get());
    if (result != NO_ERROR)
      return result;
    if (!_netResource->RemoteNameIsDefined)
      return S_OK;

    CNetFolder *netFolder = new CComObject<CNetFolder>;
	netFolder->FinalConstruct();
    CComPtr<IFolderFolder> subFolder = netFolder;
    netFolder->Init(resourceParent, 0, CHAR_PATH_SEPARATOR);
    *resultFolder = subFolder.Detach();
  }
  return S_OK;
}

STDMETHODIMP CNetFolder::GetNumberOfProperties(UINT32 *numProperties) 
{ 
	*numProperties = sizeof(kProps) / sizeof(kProps[0]); 
	return S_OK; 
} 
STDMETHODIMP CNetFolder::GetPropertyInfo(UINT32 index, BSTR *name, PROPID *propID, VARTYPE *varType) 
{ 
	if(index >= sizeof(kProps) / sizeof(kProps[0])) 
		return E_INVALIDARG; 
    const STATPROPSTG &srcItem = kProps[index]; 
    *propID = srcItem.propid; *varType = srcItem.vt; *name = 0; return S_OK; 
} 


STDMETHODIMP CNetFolder::GetFolderProperty(PROPID propID, PROPVARIANT *value)
{
  CComPropVariant prop;
  switch(propID)
  {
    case kpidType: prop = CComBSTR(_T("NetFolder")); break;
    case kpidPath: prop = CComBSTR(_path); break;
  }
  prop.Detach(value);
  return S_OK;
}

STDMETHODIMP CNetFolder::GetSystemIconIndex(UINT32 index, INT32 *iconIndex)
{
  if (index >= (UINT32)_items.size())
    return E_INVALIDARG;
  *iconIndex = 0;
  const auto & resource = _items[index];
  int iconIndexTemp;
  if (resource->DisplayType == RESOURCEDISPLAYTYPE_SERVER ||
      resource->Usage == RESOURCEUSAGE_CONNECTABLE)
  {
    if (Common::FileSystem::GetRealIconIndex(resource->RemoteName, 0, iconIndexTemp))
    {
      *iconIndex = iconIndexTemp;
      return S_OK;
    }
  }
  else
  {
    if (Common::FileSystem::GetRealIconIndex(TEXT(""), FILE_ATTRIBUTE_DIRECTORY, iconIndexTemp))
    {
      *iconIndex = iconIndexTemp;
      return S_OK;
    }
    // *anIconIndex = GetRealIconIndex(0, _T("\\\\HOME"));
  }
  return GetLastError();
}

}
}
}